package org.dbp.core.mvc.crud.controller;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;

import javax.servlet.http.HttpServletRequest;

import org.dbp.core.mvc.crud.form.CrudBaseForm;
import org.dbp.core.mvc.crud.form.CrudFiltroBaseForm;
import org.dbp.core.mvc.crud.parser.GenericParser;
import org.dbp.core.service.core.error.DbpException;
import org.dbp.core.service.service.GenericService;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.support.RequestContextUtils;
/**
 * 
 * Abstracción general del crud
 * 
 * @author dbp
 *
 * @param <F>	Formulario donde guardaremos la información del crud
 * @param <FF>	Formulario donde definiermos los campos para el filtro.
 * @param <FFLT> Donde ponemos la lista que va a ser pintada en pantalla.
 * @param <E>	Entidad de datos.
 * @param <ID>	
 * @param <EF>
 */
public class AbstractCrudController 
	<F extends CrudBaseForm<ID>
	,FF extends CrudFiltroBaseForm<FFLT>
	,FFLT extends Serializable
	,E extends Serializable
	,ID extends Serializable
	,EF extends Serializable
	>{

	private Class<F> claseF;
	private Class<FF> claseFF;
	private Class<FFLT> claseFFLT;
	private Class<E> claseE;
	private Class<ID> claseID;
	private Class<EF> claseEF;
	private String nombreId;
	
	private GenericService<E,ID,EF> service;
	private GenericParser<E,F,FFLT,FF,EF> parser;
	
	
	public AbstractCrudController(Class<F> claseF, Class<FF> claseFF,
			Class<FFLT> claseFFLT, Class<E> claseE, Class<ID> claseID,
			Class<EF> claseEF) {
		super();
		this.claseF = claseF;
		this.claseFF = claseFF;
		this.claseFFLT = claseFFLT;
		this.claseE = claseE;
		this.claseID = claseID;
		this.claseEF = claseEF;
		this.nombreId=StringUtils.uncapitalize(claseE.getSimpleName());
	}
	public GenericService<E, ID, EF> getService() {
		return service;
	}
	public void setService(GenericService<E, ID, EF> service) {
		this.service = service;
	}

	
	// INICIO
	
	public GenericParser<E, F, FFLT, FF, EF> getParser() {
		return parser;
	}
	public void setParser(GenericParser<E, F, FFLT, FF, EF> parser) {
		this.parser = parser;
	}
	@RequestMapping()
	public String  inicio(F pruebaForm){
		  return "redirect:/"+nombreId+"/filtro/inicio.htm";
	}
	
	
	// CRUD
	//ALTA
	@RequestMapping(value="/inicio/alta")
	public String inicioAlta(@ModelAttribute("form")F form) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException{
		TiposCrudUtils.cargarMetaDatos(form, TiposCrud.ALTA);
		return "/"+nombreId+"/"+nombreId;
	}
	
	
	@RequestMapping(value = "/alta", method = RequestMethod.POST)
	public String alta(@ModelAttribute("form")F pruebaForm,BindingResult bindingResult,final RedirectAttributes redirectAttributes){
		if(bindingResult.hasErrors()){
			return "/"+nombreId+"/"+nombreId;
		}else{
			redirectAttributes.addFlashAttribute("form", pruebaForm);
			return "redirect:/"+nombreId+"/procesar/alta.htm";
		}
	}
	
	@RequestMapping(value = "/procesar/alta")
	public String procesarAlta(@ModelAttribute("form")F pruebaForm,final HttpServletRequest request) throws DbpException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{
		controlDeCaducidad(request);
		pruebaForm.setId(service.insert(parser.parerEntity(pruebaForm)));
		TiposCrudUtils.cargarMetaDatos(pruebaForm, TiposResultadoCrud.ALTA);
		return "/comun/resultado";
	}
	
	// CONSULTA
	@RequestMapping(value = "/{id}/consulta")
	public String inicioConsulta(@ModelAttribute("form")F pruebaForm,@PathVariable ID id) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException{
		parser.parserForm(pruebaForm,service.findId(id));
		TiposCrudUtils.cargarMetaDatos(pruebaForm, TiposCrud.CONSULTAR);
		return "/"+nombreId+"/"+nombreId;
	}
	// OPERACION ELIMINAR
	@RequestMapping(value = "/{id}/eliminar")
	public String eliminar(@PathVariable ID id,final RedirectAttributes redirectAttributes) throws InstantiationException, IllegalAccessException{
		F pruebaForm = poblarForm();
		pruebaForm.setId(id);
		redirectAttributes.addFlashAttribute("form", pruebaForm);
		return "redirect:/"+nombreId+"/procesar/eliminar.htm";
	}
	@RequestMapping(value = "/procesar/eliminar")
	public String procesarEliminar(@ModelAttribute("form")F pruebaForm,final HttpServletRequest request) throws DbpException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{
		controlDeCaducidad(request);
		service.remove(pruebaForm.getId());
		TiposCrudUtils.cargarMetaDatos(pruebaForm, TiposResultadoCrud.ELIMINAR);
		return "/comun/resultado";
	}
	
	// OPERACION MODIFICAR
	@RequestMapping(value="/pasarModificar", method = RequestMethod.POST)
	public String pasarModificar(@ModelAttribute("form")F pruebaForm) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException{
		TiposCrudUtils.cargarMetaDatos(pruebaForm, TiposCrud.MODIFICAR);
		return "redirect:/"+nombreId+"/"+pruebaForm.getId()+"/modificar.htm";
	}
	@RequestMapping(value = "/{id}/modificar")
	public String inicioModificar(@ModelAttribute("form")F pruebaForm,@PathVariable ID id) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException{
		parser.parserForm(pruebaForm,service.findId(id));
		TiposCrudUtils.cargarMetaDatos(pruebaForm, TiposCrud.MODIFICAR);
		return "/"+nombreId+"/"+nombreId;
	}
	
	@RequestMapping(value = "/modificar", method = RequestMethod.POST)
	public String modificar(@ModelAttribute("form")F pruebaForm,BindingResult bindingResult,final RedirectAttributes redirectAttributes){
		if(bindingResult.hasErrors()){
			return "/"+nombreId+"/"+nombreId;
		}else{
			redirectAttributes.addFlashAttribute("form", pruebaForm);
			return "redirect:/"+nombreId+"/procesar/modificar.htm";
		}
	}
	@RequestMapping(value = "/procesar/modificar")
	public String procesarModificar(@ModelAttribute("form")F pruebaForm,final HttpServletRequest request) throws DbpException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{
		controlDeCaducidad(request);
		service.update(parser.parerEntity(pruebaForm));
		TiposCrudUtils.cargarMetaDatos(pruebaForm, TiposResultadoCrud.MODIFICAR);
		return "/comun/resultado";
	}

	// BUSCADOR 
	
	@RequestMapping(value = "/filtro/inicio")
	public String inicioFiltro(@ModelAttribute("filtroFrom")FF pruebaFiltroFrom){
		return "/"+nombreId+"/filtro/"+nombreId+"Filtro";
	}

	@RequestMapping(value="/filtro/buscar",method=RequestMethod.POST)
	public String resultadoFiltro(@ModelAttribute("filtroFrom")FF pruebaFiltroFrom,BindingResult bindingResult
			,final RedirectAttributes redirectAttributes){
		if(bindingResult.hasErrors()){
			return "/"+nombreId+"/filtro/"+nombreId+"Filtro";
		}else{
			redirectAttributes.addFlashAttribute("filtroFrom", pruebaFiltroFrom);
			return "redirect:/"+nombreId+"/filtro/resultado.htm";
		}
	}
	
	@RequestMapping(value="/filtro/resultado")
	public String procesarResultadoFiltro(@ModelAttribute("filtroFrom")FF pruebaFiltroForm,final HttpServletRequest request) throws Exception{
		controlDeCaducidad(request);
		pruebaFiltroForm.setListado(parser.parserListado(service.findFilter(parser.parserFilter(pruebaFiltroForm))));
		return "/"+nombreId+"/filtro/"+nombreId+"Resultado";
		//return "/"+nombreId+"/filtro/"+nombreId+"Filtro";
	}
	
	// Poblar
	
    @ModelAttribute("filtroFrom")
    public FF poblarFiltroForm() throws InstantiationException, IllegalAccessException{
    	FF valdev=claseFF.newInstance();
    	valdev.setListado(new ArrayList<FFLT>());
    	return valdev;
    }
	
    
    @ModelAttribute("form")
	public F poblarForm() throws InstantiationException, IllegalAccessException{
    	F valdev=claseF.newInstance();
    	valdev.setFuncionalidad(nombreId);
		return valdev; 
	}
	// METODOS PRIVADOS
	
    /**
     * Se encarga de controlar el error que nos pulsen un F5 dos veces.
     */
	private void controlDeCaducidad(final HttpServletRequest request)
			throws DbpException {
		if(RequestContextUtils.getInputFlashMap(request)==null){
			throw new DbpException("error.general");
		}
	}
}
